CAN Bus Adapter

The CAN Bus Adapter is a collection of tools that lets you develop and integrate tests and simulations involving communication over a CAN bus within the miniHIL platform.

The set of tools includes:

  • Runtime and model support libraries

    • Easy sending and receiving can frames to the physical can bus from any eTrice actor

    • Supports Remote Transmission Requests (RTR)

    • Supports Extended CAN Frames

  • An DBC-to-eTrice model generator

    • Automatically creates ROOM ActorClasses that support automatic encoding, decoding, packing and unpacking of can frame signals

The CAN generator is part of the gradle build. It is part of the normal assemble process. If needed the generator can also be invoked separately: .\gradlew.bat generateDBCConf. The gradle task generates room models for all files with .dbcconf extension in the model/model-user folder in the miniHilProject as well as in all configured library projects.

CAN HAL

Currently, two separate CAN instances with a pair of physical pins on the MiniHiL board each, are available.

Note The sample point is currently fixed at 75%. If this leads to problems with high CAN-FD bitrates talk to us.

Structure

Structure and dependencies of the can actors

CAN structure

How to use the HAL

  • Connect your DuT to the MiniHiL board. For blue miniHIL boards (v2.3 and higher) have a look at the pinout below. For older boards the following warning is important.

Warning On old (pre V2.3, green) miniHIL boards the pinout of the CAN SUB-D connector is mirrored. I.e. the following pins are swapped: 1 <→ 5, 2 <→ 4, 3 <→ 3, 6 <→ 9, 7 <→ 8.
can pins
canport minihil
  • Import the CAN actor.

import can.api.CANService.*
import can.platform.Stm32CANService.*
  • Create an instance of either AStm32CANService1 or AStm32CANService2 (or both) and connect the ports.

ActorRef can1: AStm32CANService1
ActorRef can2: AStm32CANService2
  • Initialize the CAN actor by sending an open(DCANParams) message to the CAN service ctl port. Make sure both nominal and data bitrate match with the DuT.

DCanParams params = {
    .baudRate_kbs = 40,
    .baudRate_kbs_fd = 125
};
// CANctl bound to ctl port of CAN service actor
CANctl.open(&params);
Important Both baudRate_kbs and baudRate_kbs_fd params must be set to valid values. Otherwise the initialization of the CAN service will fail and it will respond to the open request with a ctl.errorOpen notification message.
  • Wait until you receive the opened() message in response.

  • You are now able to send and receive CAN messages through the CANData port.

   CANMessage msg;
	msg.id = 0x00;
	msg.isExtendedFrame = false;
	//Length has to be a value of {1,2,3,4,5,6,7,8,12,16,20,24,32}
	msg.length = 16;
	msg.data[0] = 0x00;
	msg.data[1] = 0x01;
	//...
	msg.data[15] = 0x0f;
	msg.isRemoteFrame = false;
	msg.isBitRateSwitched = true;
	msg.isFDFrame = true;

   CANdata.send(&msg);

Example Application (HAL only)

This Application will initialize CAN and send one Message per second while waiting for incoming Messages.

RoomModel MiniHilProject {

	import can.api.CANService.*
	import can.platform.Stm32CANService.*
	import etrice.api.timer.PTimer

	ActorClass Application {
		Structure {

			conjugated Port CANctl: PCANCtrl
			conjugated Port CANdata: PCANData

			ActorRef can1: AStm32CANService1
			ActorRef can2: AStm32CANService2

			Binding CANdata and can1.bus
			Binding CANctl and can1.ctl

			SAP timer: PTimer
		}
		Behavior {
			StateMachine {
				State Active {
				}
				State Initializing
				State FatalError
				Transition init0: initial -> Initializing {
					action '''
						//initialize CAN with Nominal Bitrate = 40kb and Data Bitrate = 125kb
						DCanParams params = {
						    .baudRate_kbs = 40,
						    .baudRate_kbs_fd = 125
						};
						CANctl.open(&params);
					'''
				}
				Transition tr0: Initializing -> Active {
					triggers {
						<opened: CANctl>
					}
					action '''
						timer.startTimer(1000);
					'''
				}
				Transition tr1: Initializing -> FatalError {
					triggers {
						<errorOpen: CANctl>
					}
					action '''
						etLogger_logError("Failed to initialize CANServiceTest!");
					'''
				}
				Transition tr2: Active -> Active {
					triggers {
						<received: CANdata>
					}
					action '''
						// received CAN Message
						uint8 * d = transitionData->data;
					'''
				}
				Transition tr3: Active -> Active {
					triggers {
						<errorRecv: CANctl>
					}
					action '''// received incorrect CAN Message'''
				}
				Transition tr4: Active -> Active {
					triggers {
						<errorSend: CANctl>
					}
					action '''// sending CAN Message failed'''
				}
				Transition tr5: Active -> Active {
					triggers {
						<timeout: timer>
					}
					action '''
						// send CAN Message on timer timeout
						CANMessage msg;
						msg.id = 0x00;
						msg.isExtendedFrame = 0;
						//Length has to be a value of {1,2,3,4,5,6,7,8,12,16,20,24,32}
						msg.length = 16;
						msg.data[0] = 0x00;
						msg.data[1] = 0x01;
						//...
						msg.data[15] = 0x0f;
						msg.isRemoteFrame = 0;
						msg.isBitRateSwitched = true;
						msg.isFDFrame = true;

						CANdata.send(&msg);
					'''
				}
			}
		}
	}
}

Ingress filtering CAN messages by frame id Advanced

CANFiltering
Flow to enable/disable message filtering

If many actors are connected to the HAL canBus port each actor will receive a copy of the received can message. This might lead to performance issues. But, in many cases each connected actor only reacts to can messages with specific ids. In those cases ingress filtering can be used.

The PCANData Protocol supports message filtering for each connected user port. To enable filtering you have to first stop forwarding of all message by sending a disableForwarding message. Afterwards you may configure which messages your actor should receive using the enableForwardingWithFilter message with DCanFilterConfig payload. NOTE that the DCanFilterConfig only takes a pointer to a uint32 array. To cater for different filter needs (number of valid ids) the array of valid canIDs must be kept within the users actor. The PortClass directly accesses that array and does not create a copy.